package com.rightrule.rule.utils;


import com.alibaba.nacos.common.util.Md5Utils;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyCodeSource;
import groovy.lang.GroovyObject;
import groovy.lang.Script;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.security.AccessController;
import java.security.PrivilegedAction;

@Slf4j
public class ScriptUtils {
    private static final String DEFAULT_CODEBASE = "/groovy/script";


    public static String getScriptCacheKey(String scriptText) {
        return Md5Utils.getMD5(scriptText, StandardCharsets.UTF_8.name());
    }

    /**
     * Aviator编译(无需缓存)
     *
     * @param scriptText
     * @return
     */
    public static Expression compileAviatorScript(String scriptText) {
        return compileAviatorScript(scriptText, false);
    }


    /**
     * Aviator编译（可选是否缓存）
     *
     * @param scriptText
     * @param needCache
     * @return
     */
    public static Expression compileAviatorScript(String scriptText, boolean needCache) {
        if (needCache) {
            // 进行Md5避免脚本内容过长，key占用内存过多
            String scriptMd5 = getScriptCacheKey(scriptText);
            return AviatorEvaluator.getInstance().compile(scriptMd5, scriptText, true);
        }
        return AviatorEvaluator.getInstance().compile(scriptText);
    }

    /**
     * Aviator编译（可选是否缓存）
     *
     * @param cacheKey
     * @param scriptText
     * @param needCache
     * @return
     */
    public static Expression compileAviatorScript(String cacheKey, String scriptText, boolean needCache) {
        if (needCache) {
            return AviatorEvaluator.getInstance().compile(cacheKey, scriptText, true);
        }
        return AviatorEvaluator.getInstance().compile(scriptText);
    }

    /***              Groovy            ***/
    public static GroovyObject compileGroovyScriptAndGet(String scriptText) {
        Class<Script> groovyClass = compileGroovyScript(scriptText);
        try {
            return (GroovyObject) groovyClass.newInstance();
        } catch (InstantiationException instantiationException) {
            log.info("instantiationException e={}", instantiationException);
        } catch (IllegalAccessException illegalAccessException) {
            log.info("illegalAccessException e={}", illegalAccessException);
        }
        return null;
    }

    /**
     * 编译groovy脚本
     *
     * @param scriptText
     * @return
     */
    public static Class<Script> compileGroovyScript(String scriptText) {
        GroovyClassLoader classLoader = new GroovyClassLoader();
        return classLoader.parseClass(scriptText);
    }

    /**
     * 编译groovy脚本并且缓存
     *
     * @param scriptText
     * @param needCache
     * @return
     */
    public static Class<Script> compileGroovyScript(String scriptText, boolean needCache) {
        if (needCache) {
            String cacheKey = getRamdonGroovyClassName(scriptText);
            return compileGroovyScript(cacheKey, scriptText, true);
        }
        return compileGroovyScript(scriptText);
    }

    private static String getRamdonGroovyClassName(String scriptText) {
        int hashCode = scriptText.hashCode();
        int absHashCode = ((hashCode < 0) ? -hashCode : hashCode);
        return "script" + System.currentTimeMillis() + absHashCode + ".groovy";
    }

    private static Class<Script> compileGroovyScript(String cacheKey, String scriptText, boolean needCache) {
        GroovyClassLoader classLoader = new GroovyClassLoader();
        GroovyCodeSource gcs = AccessController.doPrivileged(
                (PrivilegedAction<GroovyCodeSource>) () ->
                        new GroovyCodeSource(scriptText, cacheKey, DEFAULT_CODEBASE));
        gcs.setCachable(needCache);

        return classLoader.parseClass(gcs);
    }

}
